import json
import os
import shutil
import time
from urllib import request
import tornado
from tornado import gen
from tornado import web

import sys
sys.path.append("..")

from .base import BaseHandler
from utils.pathutil import PathBuilder
from utils.dbutil import MySQLConnection
from utils.dockerutil import JupyterServiceUtil
from utils.jsonresult import JsonResult, json_result_encoder

# 用于实验室课程列表页面的处理程序
class CoursesHandler(BaseHandler):
    @web.authenticated
    @gen.coroutine
    def get(self):
        user = self.get_current_user()
        status = self.get_argument('status', default='')
        courseid = self.get_argument('courseid', default=0)

        template_name = 'courses.html'
        if status:
            template_name = 'coursesactive.html'
        elif courseid:
            template_name = 'labsactive.html'

        self.render(template_name, user=user)

class LabHandler(BaseHandler):
    @web.authenticated
    @gen.coroutine
    def get(self):
        user = self.get_current_user()
        
        operation = self.get_argument('operation', default='')
        operand =  self.get_argument('operand', default='')

        json_result = JsonResult()

        call_func = dispatch_map.get(operation)
        if (not operation) or (call_func is None):
            json_result.success = False
            json_result.message = '无效的操作'
        else:
            call_func(user['accountname'], operand, json_result)

        self.write(json.dumps(json_result, ensure_ascii=True, default=json_result_encoder))
        self.finish()
    
   
    # 获取该学生参加的所有课程和实验列表
    @staticmethod
    def get_all_labs(accountname, operand, json_result):
        conn = MySQLConnection.get_connection()
        try:
            with conn.cursor() as cursor:
                sql = "select accountid from account where accountname='{}'"
                cursor.execute(sql.format(accountname))
                accountid = cursor.fetchone()['accountid']
                sql = 'select a.courseid, a.coursename, a.code as coursecode, b.chapterid, \
                    b.chaptername, b.code as chaptercode,c.labid, c.labname, c.labtype, c.code as labcode, c.enabled \
                    from course as a left join chapter as b on a.courseid=b.courseid \
                    left join lab as c on b.chapterid=c.chapterid \
                    where a.courseid in (select distinct course.courseid from course \
                        inner join class on course.courseid=class.courseid \
                        inner join classstudent on class.classid=classstudent.classid \
                        where classstudent.accountid={}) \
                    order by a.courseid, b.chapterid, c.labid'
                cursor.execute(sql.format(accountid))
                json_result.data = cursor.fetchall()
        finally:
            conn.close()
    

    # 获取该学生当前尚未完结的课程和实验列表
    @staticmethod
    def get_active_courses(accountname, operand, json_result):
        conn = MySQLConnection.get_connection()
        try:
            with conn.cursor() as cursor:
                sql = "select accountid from account where accountname='{}'"
                cursor.execute(sql.format(accountname))
                accountid = cursor.fetchone()['accountid']
                sql = "SELECT a.classname, DATE_FORMAT(a.startdate,'%Y-%m-%d') AS startdate, \
                    DATE_FORMAT(a.enddate,'%Y-%m-%d') AS enddate, b.courseid, b.coursename \
                    FROM class AS a INNER JOIN course AS b ON a.courseid=b.courseid \
                    INNER JOIN classstudent AS c on a.classid=c.classid \
                    WHERE c.accountid={} AND a.status=1 ORDER BY enddate"
                cursor.execute(sql.format(accountid))
                json_result.data = cursor.fetchall()
        finally:
            conn.close()


    # 获取该学生选中的课程下所有的实验列表
    @staticmethod
    def get_course_labs(accountname, operand, json_result):
        courseid = operand
        conn = MySQLConnection.get_connection()
        try:
            with conn.cursor() as cursor:
                sql = 'select coursename from course where courseid=' + courseid
                cursor.execute(sql)
                coursename = cursor.fetchone()['coursename']

                sql = 'select chapterid, chaptername, code from chapter where courseid=' + courseid
                cursor.execute(sql)
                chapters = cursor.fetchall()
                
                sql = 'select a.chapterid, b.labid, b.labname, b.code, b.labtype, b.enabled \
                    from chapter as a inner join lab as b on a.chapterid=b.chapterid where a.courseid=' + courseid
                cursor.execute(sql)
                labs = cursor.fetchall()

                json_result.data = {'coursename':coursename, 'chapters':chapters, 'labs':labs}
        finally:
            conn.close()
    
    # 确保实验对应的文件目录准备就绪
    @staticmethod
    def ensure_lab_files(accountname, operand, json_result):
        lab_code = operand

        library_lab_dir = PathBuilder.get_library_dir_for_lab(lab_code)
        if not os.path.exists(library_lab_dir):
            json_result.success = False
            json_result.message = '错误的实验路径'
            return

        stu_lab_dir = PathBuilder.get_lab_work_dir_for_student(accountname, lab_code)
        if not os.path.exists(stu_lab_dir):
            # 将实验文件从library下拷贝到学生实验目录下
            try:
                # 目录整体拷贝
                shutil.copytree(library_lab_dir, stu_lab_dir)
            except:
                json_result.success = False
                json_result.message = '装载实验文件失败'
    

    # 确保实验对应的容器启动
    @staticmethod
    def ensure_lab_container(accountname, operand, json_result):
        lab_code = operand
        labs_dir = PathBuilder.get_labs_dir_for_student(accountname)
        work_dir = PathBuilder.get_lab_work_dir_for_student(accountname, lab_code)

        if not os.path.exists(work_dir):
            json_result.success = False
            json_result.message = '错误的实验目录'
            return

        # 获取实验对应的容器镜像名称
        conn = MySQLConnection.get_connection()
        try:
            with conn.cursor() as cursor:
                sql = 'select b.imagename from course as a inner join image as b on a.imageid=b.imageid where a.code={}'
                sql_formatted = sql.format(lab_code[0:2])
                cursor.execute(sql_formatted)
                image_name = cursor.fetchone()['imagename']
        finally:
            conn.close()

        service_name = JupyterServiceUtil.build_jupyter_service_name(accountname, image_name)
        image_full_name = JupyterServiceUtil.get_image_full_name(image_name)         
        json_result.data = service_name

        user_jupyter_service_name, _ = JupyterServiceUtil.find_user_jupyter_services(accountname)
        if user_jupyter_service_name is not None:   # 该用户已经使用了某个jupyter容器实例
            # 检查原有的jupyter服务是否与当前所需的容器一致
            if user_jupyter_service_name == service_name:       # 一致，无需销毁原有容器实例，也不必创建新容器实例
                pass
            else:                                               # 不一致，删除原有的Service
                JupyterServiceUtil.remove_service(user_jupyter_service_name)    
                # 创建jupyter容器服务
                json_result.success = JupyterServiceUtil.create_service(service_name, image_full_name, labs_dir)
        else:                                       # 该用户尚未使用任何jupyter容器实例
            json_result.success = JupyterServiceUtil.create_service(service_name, image_full_name, labs_dir)

        if not json_result.success:
            json_result.message = '容器创建错误'


    # 获取实验指南
    @staticmethod
    def get_lab_guides(accountname, operand, json_result):
        lab_code = operand
        conn = MySQLConnection.get_connection()
        try:
            with conn.cursor() as cursor:
                sql = 'select a.courseid, a.coursename, b.chapterid, b.chaptername, \
                    c.labid, c.labname, c.labtype, c.guides \
                    from course as a left join chapter as b on a.courseid=b.courseid \
                    left join lab as c on b.chapterid=c.chapterid \
                    where c.code={}'
                sql_formatted = sql.format(lab_code)
                cursor.execute(sql_formatted)
                json_result.data = cursor.fetchone()
        finally:
            conn.close()
    
    # 检查jupyer notebook server是否已经就绪
    @staticmethod
    def check_notebook_ready(accountname, operand, json_result):
        service_name = operand
        _, port = JupyterServiceUtil.get_service_state_and_port(service_name)
        # jupyter_url = "http://{}:{}/notebooks/{}/{}.ipynb".format(tornado.options.options.docker_swarm_manager_ip, port, lab_code, lab_code)
        jupyter_url = "http://{}:{}/notebooks".format(tornado.options.options.docker_swarm_manager_ip, port)
        req = request.Request(jupyter_url)
        try:
            res = request.urlopen(req)
            res.close()
            json_result.data = jupyter_url
        except:
            json_result.data = ''
    


# 处理该实验的主页面
class LabHomeHandler(BaseHandler):
    @web.authenticated
    @gen.coroutine
    def get(self):
        user = self.get_current_user()
        code = self.get_argument('code', default='')
        html = ""

        # 获取实验类型
        conn = MySQLConnection.get_connection()
        try:
            with conn.cursor() as cursor:
                sql = "select labtype, labfile from lab where code='{}'".format(code)
                cursor.execute(sql)
                result = cursor.fetchone()
                labtype = result['labtype']
                labfile = result['labfile']
        finally:
            conn.close()

        html = self.render_string('lab.html',
                    user=user,
                    code=code,
                    labfile=labfile,
                    showassignment=(labtype==2))

        self.finish(html)


# 处理该实验作业
class LabAssignmentHandler(BaseHandler):
    @web.authenticated
    @gen.coroutine
    # 检查作业是否已经提交
    def get(self):
        user = self.get_current_user()
        lab_code = self.get_argument('labcode')
        result = False
        
        conn = MySQLConnection.get_connection()
        try:
            with conn.cursor() as cursor:
                sql = "select assignmentid from assignment where labcode='{}' and accountname='{}'".format(lab_code, user['accountname'])
                cursor.execute(sql)
                assignmentid = cursor.fetchone()
                if assignmentid:
                    result = True
        finally:
            conn.close()
        
        self.write(json.dumps(result, ensure_ascii=True))
        self.finish()
    

    @web.authenticated
    @gen.coroutine
    # 提交作业
    def post(self):
        user = self.get_current_user()
        lab_code = self.get_argument('labcode')
        current_time = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))
        result = False
        
        conn = MySQLConnection.get_connection()
        try:
            with conn.cursor() as cursor:
                sql = "select assignmentid from assignment where labcode='{}' and accountname='{}'".format(lab_code, user['accountname'])
                cursor.execute(sql)
                assignmentid = cursor.fetchone()
                if assignmentid:            # 该作业曾经提交过
                    sql = "update assignment set submittime='{}' where assignmentid={}".format(current_time, assignmentid['assignmentid'])
                else:                       # 该作业尚未提交
                    sql = "insert into assignment(labcode, accountname, submittime) values('{}','{}','{}')".format(lab_code, user['accountname'], current_time)
                cursor.execute(sql)
                conn.commit()
                result = True
        finally:
            conn.close()
        
        self.write(json.dumps(result, ensure_ascii=True))
        self.finish()


dispatch_map = {
    'get_all_labs' : LabHandler.get_all_labs,
    'get_active_courses' : LabHandler.get_active_courses,
    'get_course_labs' : LabHandler.get_course_labs,
    'ensure_lab_files' : LabHandler.ensure_lab_files,
    'ensure_lab_container' : LabHandler.ensure_lab_container,
    'get_lab_guides' : LabHandler.get_lab_guides,
    'check_notebook_ready' : LabHandler.check_notebook_ready
}

default_handlers = [
    (r'/courses', CoursesHandler),
    (r'/lab', LabHandler),
    (r'/lab_home', LabHomeHandler),
    (r'/lab_assignment', LabAssignmentHandler)
]